/*
 * ServerConnectionHandler.java
 * 
 * @version 1.0, March 22, 2011
 * CS 2340 Team 10: Cyberdyne Systems
 */
package edu.gatech.oad.cyberdoc.net;

import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONException;
import org.json.JSONObject;

import edu.gatech.oad.cyberdoc.domain.User;
import edu.gatech.oad.cyberdoc.domain.action.ActionHandleException;

/**
 * Handles an individual request from the user. When the user initially opens
 * the connection with the server, a new instance of this class is created to
 * handle that connection. When the request has been completed and the response
 * has been sent, this instance is destroyed.
 * 
 * @author John Girata
 * @version 1.0, March 22, 2011
 */
public class ServerConnectionHandler implements Runnable {

	/**
	 * This class's logger.
	 */
	private static final Logger LOG;

	/**
	 * The socket associated with this connection
	 */
	private final Socket socket;

	static {
		LOG = Logger.getLogger("edu.gatech.oad.cyberdoc.net."
				+ "ServerConnectionHandler");
	}

	/**
	 * Creates an instance to manage the user on the given socket in a separate
	 * thread.
	 * 
	 * @param socket
	 *            The socket associated with this connection
	 */
	public ServerConnectionHandler(Socket socket) {
		this.socket = socket;
	}

	/**
	 * Handles the connection in a new thread.
	 */
	@Override
	public void run() {
		LOG.info("Starting handler in thread \""
				+ Thread.currentThread().getName() + "\"");

		if (socket.isClosed()) {
			LOG.severe("Socket closed; stopping");
			return;
		}

		PrintStream output = null;

		try {
			final Scanner input = new Scanner(socket.getInputStream());
			output = new PrintStream(socket.getOutputStream());

			final String string = input.nextLine();
			LOG.info("Received message " + string + " from client");

			final JSONObject request = new JSONObject(string);
			final JSONObject response = User.handleCommand(request);
			LOG.info("Sending response " + response);
			
			output.println(response.toString());
		} catch (IOException e) {
			LOG.log(Level.SEVERE, "Unexpected IOException", e);
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Bad request from user", e);
		} catch (ActionHandleException e) {
			LOG.log(Level.SEVERE, "Error processing client request", e);
			sendError(output, e);
		}
	}

	/**
	 * Encodes the relevant information from an Exception into a JSONObject and
	 * transmits it as the response to the client.
	 * 
	 * @param output
	 *            The output stream to the client
	 * @param t
	 *            The exception to send
	 */
	private void sendError(PrintStream output, Exception t) {
		final JSONObject object = scrub(new JSONObject(t));
		output.println(object);
	}

	/**
	 * Removes fields from a JSONObject encoding an Exception that would not be
	 * appropriate to send to the user.
	 * 
	 * Currently, only the stack trace is removed.
	 * 
	 * @param object
	 *            The raw encoding of the Exception
	 * @return The object without unwanted data
	 */
	private static JSONObject scrub(JSONObject object) {
		try {
			if (object.has("stackTrace")) {
				object.remove("stackTrace");
				object.put("cause", scrub(object.getJSONObject("cause")));
			}
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return object;
	}

	/**
	 * Returns a string representation of this object.
	 * 
	 * @return This object as a string
	 */
	@Override
	public String toString() {
		return "ServerConnectionHandler[port="
				+ ServerConnectionManager.getPort() + "]";
	}
}
